using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;

namespace GameOfLife
{
    public class Board
    {
        bool[,] cellMatrix;
        Random rand = new Random();
        
        /// <summary>
        /// Creates a cell matrix with all dead cells
        /// </summary>
        /// <param name="sizeX">The number of cells across</param>
        /// <param name="sizeY">The number of cells down</param>
        public Board(int sizeX, int sizeY)
        {
            cellMatrix = new bool[sizeX,sizeY];
        }


        /// <summary>
        /// Creates a cell matrix with all dead cells
        ///  except for the cell specified by the coordinates
        ///  of the POINTS parameter
        /// 
        ///   For example:
        ///    if points = [ {1,2} , {0,0}, {3,2} ]
        ///       sizeX = 4
        ///       sizeY = 3
        ///   
        ///    the cell matrix would look like
        ///      
        ///           +----+
        ///           |1000|
        ///           |0000|       
        ///           |0101|
        ///           +----+
        /// 
        /// 
        /// </summary>
        /// <param name="points">the coordinates of the cells that are alive</param>
        /// <param name="sizeX">the number of cells across</param>
        /// <param name="sizeY">the number of cells down</param>
        public Board(Point[] points, int sizeX, int sizeY){
            cellMatrix = new bool[sizeX, sizeY];
            ClearBoard();
            foreach (Point p in points){
                cellMatrix[p.X, p.Y]=true;
            }
         }


        /// <summary>
        /// Creates a cell matrix based on the contents
        ///  of a text file
        /// 
        /// The first two lines should give the demensions
        ///  of the cell matrix. The first line is the number
        ///  of cells across and the second line is the number
        ///  of cells down
        /// The remaing lines define the cell matrix. 0
        ///  represents a dead cell and 1 represents a
        ///  living cell
        /// 
        ///
        /// 
        ///   For exmaple:
        /// 
        ///     4                 +----+
        ///     3      makes      |1000|
        ///     1000    === \     |0000|       
        ///     0000    === /     |0101|
        ///     0101              +----+
        ///
        /// 
        /// </summary>
        /// <param name="filename">the name of the file</param>
        public Board(string filename)
        {
            //opens the file into a streamreader object
            try{
	        using (StreamReader re = new StreamReader(filename)){
                    int rows, columns;
                    string line;
            
                    //parse the first 2 lines of the file, they should
                    //correspond to the rows and columns of the matrix
                    line = re.ReadLine();
                    columns = int.Parse(line);
                    line = re.ReadLine();
                    rows = int.Parse(line);
            
                    //initialize the matrix and set all values to false
                    cellMatrix = new bool[columns, rows];
                    ClearBoard();
            
                   //for each row, read the corresponding line in the file
                    for(int j = 0; j < rows; j++){
                        line = re.ReadLine();
                   //for each line, check which positions are equal to 1
                   //and set the corresponding position in the matrix
                   //to true
                        for(int i = 0; i < columns; i++){
                            if (line[i] == '1')
                                cellMatrix[i,j] = true;
                        }
                    }
                }
            }
            catch (Exception e){
                    Console.WriteLine("The file could not be read:");
                    Console.WriteLine(e.Message);
            }
        }



        /// <summary>
        /// This method sets all of the cells
        ///  in cellMatrix to dead (false)
        /// </summary>
        public void ClearBoard(){
            for (int y = 0; y < SizeY(); y++){
                for (int x = 0; x < SizeX(); x++){
                   this.cellMatrix[x, y] = false;
                }
            }
        }


        /// <summary>
        /// This creates a random cell matrix
        ///  by giving each cell in cellMatrix an 
        ///  equal chance of being alive (true)
        ///  or dead (false)
        /// </summary>
        public void RandomBoard()
        {
            for (int y = 0; y < SizeY(); y++){
                for (int x = 0; x < SizeX(); x++){
                    cellMatrix[x,y]=(rand.Next(0,2)==1);
                }
            }
        }



        /// <summary>
        /// 
        /// This method updates the cellMatrix by updating it
        ///  to the next time step
        /// 
        ///  This is done by visiting each cell in cellMatrix to count the
        ///   number of its neighbors that are living, then
        ///   using that number and the ruleset to deterning if 
        ///   that cell should be dead
        ///   or alive in the updated cellMatrix
        /// 
        /// Hint:
        /// Use RuleSet's NextState() method
        ///  and Board's NumNeighbors() method
        /// 
        /// </summary>
        /// <param name="ruleset">
        /// the ruleset used to determine how many neighbors
        ///  need to be alive for a cell to be alive during
        ///  the next time step
        /// </param>
        public void Step(RuleSet ruleset){
            bool[,] temp = new bool[SizeX(), SizeY()];
            for (int y = 0; y < SizeY(); y++){
                for (int x = 0; x < SizeX(); x++){
                    temp[x,y]=ruleset.NextState(GetCell(x,y), NumNeighbors(x,y));
                }
            }
            cellMatrix=temp;
        }

        /// <summary>
        /// This method counts the number of living cells
        ///  that are adjacent to a given cell
        /// 
        ///  Each cells has 8 adjacent cells
        ///        123
        ///        8*4
        ///        765
        /// 
        ///  Note: There are no boundaries because the matrix
        ///   is toroidally wrapped.  This means that the
        ///   left most cells are adjacent to the right most
        ///   cells and the top cells are adjacent to the
        ///   bottom cells.
        /// 
        /// parameters i and j specify the location
        ///  of the given cell in cellMatrix
        /// </summary>
        /// <param name="i"></param>
        /// <param name="j"></param>
        /// <returns></returns>
        public int NumNeighbors(int i, int j){
            int count=0;
            for (int y=j-1; y<=j+1; y++){
                for (int x=(i-1); x<=(i+1); x++){
                    if ((x==i)&&(y==j)) continue;
                    if (GetCell((SizeX()+x)%SizeX(), (SizeY()+y)%SizeY())) count++;
                }
            }
            return count;
        }

        
        /// <summary>
        /// This method is used for getting
        ///  the dimensions of the cellMatrix
        /// 
        /// Hint: this should be a one line function
        ///  if you use bool[][]'s GetLength() method
        /// </summary>
        /// <returns>the number of cells across the cell Matrix</returns>
        public int SizeX(){
            return cellMatrix.GetLength(0);
        }

        /// <summary>
        /// This method is used for getting
        ///  the dimensions of the cellMatrix
        /// 
        /// Hint: this should be a one line function
        ///  if you use bool[][]'s GetLength() method
        /// </summary>
        /// <returns>the number of cells down the cell Matrix</returns>
        public int SizeY(){
            return cellMatrix.GetLength(1);
        }

        

        /// <summary>
        /// This method is called to determine
        ///  if a given cell is dead or alive
        /// 
        /// parameters x and y specify the location
        ///  of the given cell
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>true if the cell is alive, false if the cell is dead</returns>
        public bool GetCell(int x, int y)
        {
            return cellMatrix[x, y];
        }
    }
}
